﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel.Composition;
using System.Collections.Generic;

using Caliburn.Micro.BikeWizard.Tests.BaseClasses;
using Caliburn.Micro.BikeWizard.Tests.Utils;
using Caliburn.Micro.BikeWizard.Views;
using Caliburn.Micro.BikeWizard.Utils;

namespace Caliburn.Micro.Wizard.Tests
{
    /// <summary>
    /// Testclass that illustrates how you can test whether data bindings between view and viewmodel have been set up correctly
    /// 
    /// The tests rely on the testdata being set up correctly. This is done in by the TestDataHelper, which is instantiated in the bootstrapper.
    /// If you change the testdata setup you will have to change the unittests.
    /// 
    /// Notes:
    /// 
    /// 1) The test code shows how to use Caliburn Micro's Co-Routines support to make writing the tests a bit easier. 
    /// 2) The Sleeper() calls serve no other purpose than to slow down the UI a bit, so you can see what's happening on the silverlight test canvas.  
    /// 3) It's probably better to write smaller tests instead of one big one like was done here.
    /// 4) It would be better if we could write IoC.Get<ExportFactory<BikeWizardViewModel>>() after registering the ExportFactory in the MEF 
    ///    container, instead of creating a wrapper Factory (i.e. BikeWizardViewModelFactory), but I couldn't get it to work. Anybody? :)
    /// </summary>
    [TestClass]
    public class BindingTests : TestBase
    {
        #region Initialization
        /// <summary>
        /// Important: Any exceptions that occur in the Init() method are swallowed
        /// </summary>
        [TestInitialize]
        [Asynchronous]
        public void InitTest()
        {
            new CoroutineExecutor(
                bikeOptionService.ClearData(),
                new TestDataHelper().SetupTestData(bikeOptionService),
                SetTestComplete()).Execute(null);
        }
        #endregion

        #region Tests
        [TestMethod]
        [Asynchronous]
        public void BikeWizardViewModel_BindingTest()
        {
            new CoroutineExecutor(BikeWizardViewModel_BindingTestImpl()).Execute(null);
        }
        #endregion

        #region Test Implementations
        /// <summary>
        /// Simple tests that navigates through the wizard and checks whether the view controls are bound correctly to the viewmodel properties
        /// I might have forgotten to test some things, but you get the idea.
        /// </summary>
        /// <returns></returns>
        private IEnumerable<IResult> BikeWizardViewModel_BindingTestImpl()
        {
            var bikeWizardViewModelFactory = IoC.Get<BikeWizardViewModelFactory>();
            var wizardViewModel = bikeWizardViewModelFactory.CreateInstance();
            var wizardView = CreateViewForViewModel<BikeWizardView>(wizardViewModel);

            yield return
                new MultipleEventWaiter(
                    () => 
                    {
                        this.TestPanel.Children.Add(wizardView);
                    },
                    new SingleEventWaiter(wizardView, "Loaded"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    ItemsControl wizardStepsControl = GetUiElement<ItemsControl>("Items");
                    Assert.IsNotNull(wizardStepsControl);
                    Assert.AreEqual(4, wizardStepsControl.Items.Count);

                    ContentControl activeItem = GetUiElement<ContentControl>("ActiveItem");
                    Assert.IsNotNull(activeItem);
                    Assert.IsTrue(activeItem.Content is WelcomeView);

                    Button moveNextButton = GetUiElement<Button>("MoveNext");
                    Assert.IsNotNull(moveNextButton);
                    Assert.IsTrue(moveNextButton.IsEnabled);

                    Button movePreviousButton = GetUiElement<Button>("MovePrevious");
                    Assert.IsNotNull(movePreviousButton);
                    Assert.IsFalse(movePreviousButton.IsEnabled);

                    Button cancelButton = GetUiElement<Button>("Cancel");
                    Assert.IsNotNull(cancelButton);
                    Assert.IsTrue(cancelButton.IsEnabled);

                    actionResult.SetComplete(null);
                });

            yield return new Sleeper(2);

            yield return
                new MultipleEventWaiter(
                    () => wizardViewModel.MoveNext(),
                    new SingleEventWaiter(wizardViewModel, "ActivationProcessed"),
                    new SingleEventWaiter(wizardView, "Loaded"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    ContentControl activeItem = GetUiElement<ContentControl>("ActiveItem");
                    Assert.IsNotNull(activeItem);
                    Assert.IsTrue(activeItem.Content is BikeTypeFrameSizeView);

                    Button moveNextButton = GetUiElement<Button>("MoveNext");
                    Assert.IsNotNull(moveNextButton);
                    Assert.IsFalse(moveNextButton.IsEnabled);

                    // Select the first frame size option
                    Assert.IsNotNull(activeItem.Content as UserControl);
                    var bikeTypeFrameSizeViewModel = (activeItem.Content as UserControl).DataContext as BikeTypeFrameSizeViewModel;
                    Assert.IsNotNull(bikeTypeFrameSizeViewModel);
                    Assert.IsNotNull(bikeTypeFrameSizeViewModel.AvailableFrameSizes);
                    Assert.IsTrue(bikeTypeFrameSizeViewModel.AvailableFrameSizes.Count > 0);
                    bikeTypeFrameSizeViewModel.AvailableFrameSizes[0].IsSelected = true;

                    // Now the next button should be enabled
                    Assert.IsTrue(moveNextButton.IsEnabled);

                    Button movePreviousButton = GetUiElement<Button>("MovePrevious");
                    Assert.IsNotNull(movePreviousButton);
                    Assert.IsTrue(movePreviousButton.IsEnabled);

                    Button cancelButton = GetUiElement<Button>("Cancel");
                    Assert.IsNotNull(cancelButton);
                    Assert.IsTrue(cancelButton.IsEnabled);

                    actionResult.SetComplete(null);
                });

            yield return new Sleeper(2);
            yield return
                new MultipleEventWaiter(
                    () => wizardViewModel.MoveNext(),
                    new SingleEventWaiter(wizardViewModel, "ActivationProcessed"),
                    new SingleEventWaiter(wizardView, "Loaded"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    ContentControl activeItem = GetUiElement<ContentControl>("ActiveItem");
                    Assert.IsNotNull(activeItem);
                    Assert.IsTrue(activeItem.Content is BikeExtrasView);

                    Button moveNextButton = GetUiElement<Button>("MoveNext");
                    Assert.IsNotNull(moveNextButton);
                    Assert.IsTrue(moveNextButton.IsEnabled);

                    Button movePreviousButton = GetUiElement<Button>("MovePrevious");
                    Assert.IsNotNull(movePreviousButton);
                    Assert.IsTrue(movePreviousButton.IsEnabled);

                    Button cancelButton = GetUiElement<Button>("Cancel");
                    Assert.IsNotNull(cancelButton);
                    Assert.IsTrue(cancelButton.IsEnabled);

                    actionResult.SetComplete(null);
                });

            yield return new Sleeper(2);
            yield return
                new MultipleEventWaiter(
                    () => wizardViewModel.MoveNext(),
                    new SingleEventWaiter(wizardViewModel, "ActivationProcessed"),
                    new SingleEventWaiter(wizardView, "Loaded"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    ContentControl activeItem = GetUiElement<ContentControl>("ActiveItem");
                    Assert.IsNotNull(activeItem);
                    Assert.IsTrue(activeItem.Content is BikeSummaryView);

                    Button moveNextButton = GetUiElement<Button>("MoveNext");
                    Assert.IsNotNull(moveNextButton);
                    Assert.AreEqual("Finish", moveNextButton.Content);
                    Assert.IsTrue(moveNextButton.IsEnabled);

                    Button movePreviousButton = GetUiElement<Button>("MovePrevious");
                    Assert.IsNotNull(movePreviousButton);
                    Assert.IsTrue(movePreviousButton.IsEnabled);

                    Button cancelButton = GetUiElement<Button>("Cancel");
                    Assert.IsNotNull(cancelButton);
                    Assert.IsTrue(cancelButton.IsEnabled);

                    actionResult.SetComplete(null);
                });

            yield return new Sleeper(2);

            yield return SetTestComplete();
        }
        #endregion
    }
}